﻿using System;
using System.Linq;
using System.Collections.ObjectModel;
using MassPokerTool.Infrastructure.Extensions;
using MassPokerTool.Infrastructure.Hooking.Target;
using System.Collections.Generic;
using System.Diagnostics;

namespace MassPokerTool.Infrastructure.Hooking
{
    public class AppsManager
    {
        private static readonly IList<int> HookedPids; // due to optimization of IsHooked method which is being called often
        private static readonly object SyncRoot;

        static AppsManager()
        {
            SyncRoot = new object();
            HookedPids = new List<int>();
        }

        public static void Initialize(IList<ITargetApplication> applications)
        {
            Applications = new ReadOnlyCollection<ITargetApplication>(applications);
        }

        public static void Refresh()
        {
            lock (SyncRoot)
            {
                // remove processes that are not running anymore
                foreach (var application in Applications.Where(a => a.IsRunning))
                {
                    try
                    {
                        Process.GetProcessById(application.CurrentPid);
                    }
                    catch (ArgumentException) 
                    {
                        if (HookedPids.Contains(application.CurrentPid))
                            HookedPids.Remove(application.CurrentPid);
                        application.Down();
                    }
                }
                // add newly started processes
                foreach (var application in Applications.Where(a => !a.IsRunning))
                {
                    int pid;
                    if (ProcessIsRunning(application.ProcessName, out pid))
                    {
                        application.Up(pid);
                    }
                }
            }
        }

        private static bool ProcessIsRunning(string processName, out int pid)
        {
            pid = 0;
            var processes = Process.GetProcessesByName(processName);
            if (!processes.IsNullOrEmpty())
            {
                pid = processes.First().Id;
                return true;
            }
            return false;
        }

        public static bool IsHooked(int inClientPid)
        {
            lock (SyncRoot)
            {
                return HookedPids.Contains(inClientPid);
            }
        }

        public static void ConfirmInjection(int pid)
        {
            lock (SyncRoot)
            {
                var foundApp = Applications.SingleOrDefault(a => a.CurrentPid == pid);
                if (foundApp == null)
                    throw new ApplicationException("Cannot start injection - application is not running.");
                HookedPids.Add(pid);
                foundApp.IsHooked = true;
            }
        }

        public static void QuitInjection(int pid)
        {
            lock (SyncRoot)
            {
                var foundApp = Applications.SingleOrDefault(a => a.CurrentPid == pid);
                if (foundApp == null)
                    throw new ApplicationException("Cannot quit injection - application is not running.");
                HookedPids.Remove(pid);
                foundApp.IsHooked = false;
            }
        }

        public static ReadOnlyCollection<ITargetApplication> Applications { get; private set; }
    }
}
